検証用のサイトを公開し、2週間後にAWS WAFのログを確認してみた
適当なサイトを立ち上げAWS WAFを設定し、WAFのログを確認してみました。ログ関連の運用のイメージを掴む一助になれば幸いです。
検証用のサイト
ALBの固定レスポンスで、Hello Worldを表示するサイトを作成しました。DNSドメインを取得し、私物のドメインで接続できるようにしました。
AWS WAFには以下のマネージドルールをブロックモードで設定しました。
- AWS-AWSManagedRulesAdminProtectionRuleSet(管理ページへの外部アクセスをブロック)
- AWS-AWSManagedRulesCommonRuleSet(コアルールセット)
- AWS-AWSManagedRulesKnownBadInputsRuleSet(脆弱性の悪用または発見に関連するリクエストパターンをブロック)
- AWS-AWSManagedRulesLinuxRuleSet(Linux固有の脆弱性の悪用に関連するリクエストパターンをブロック)
マネージドルールをEditすると、ルールを確認できます。コアルールセットには以下のようなルールがあります。例えば、NoUserAgent_HEADERはUser-Agentヘッダーのないリクエストをブロックします。Override rules actionを有効にすると、コアルールセット全体ではブロックで、NoUserAgent_HEADERルールはカウントするといった動きにできます。
ログの確認
Athenaでテーブルを作成します。LOCATIONは適宜、変更します。
CREATE EXTERNAL TABLE `waf_logs`( `timestamp` bigint, `formatversion` int, `webaclid` string, `terminatingruleid` string, `terminatingruletype` string, `action` string, `terminatingrulematchdetails` array< struct< conditiontype:string, location:string, matcheddata:array<string> > >, `httpsourcename` string, `httpsourceid` string, `rulegrouplist` array<string>, `ratebasedrulelist` array< struct< ratebasedruleid:string, limitkey:string, maxrateallowed:int > >, `nonterminatingmatchingrules` array< struct< ruleid:string, action:string > >, `httprequest` struct< clientip:string, country:string, headers:array< struct< name:string, value:string > >, uri:string, args:string, httpversion:string, httpmethod:string, requestid:string > ) ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe' WITH SERDEPROPERTIES ( 'paths'='action,formatVersion,httpRequest,httpSourceId,httpSourceName,nonTerminatingMatchingRules,rateBasedRuleList,ruleGroupList,terminatingRuleId,terminatingRuleMatchDetails,terminatingRuleType,timestamp,webaclId') STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat' LOCATION 's3://awswaf-bucketname/2021/01/'
ブロックしたリクエストの一覧を表示します。
SELECT * FROM waf_logs WHERE action='BLOCK' ORDER BY timestamp
結果をCSVファイルでダウンロードします。
ALBとWAFは2週間起動していたのですが、785件のブロックが発生していました。786行のうち、1行目は項目名です。
$ wc -l awswaflog.csv 786 awswaflog.csv $
ログを見ると、ホストヘッダーにIPアドレスが入っているケースがあります。おそらく、AWSのIPレンジに対して、スキャンや攻撃をしていると思われます。本来であれば、私が設定したDNS名が入っているはずです。
headers=[{name=host, value=203.0.113.1}]
CSVファイルからホストヘッダーがIPアドレスのものを除外すると、333件残りました。
$ sed -E "/name=Host, value=(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9;5D])/d" awswaflog.csv > awswaflog_exclude_hostheader-ipaddress.csv $ wc -l awswaflog_exclude_hostheader-ipaddress.csv 333 awswaflog_exclude_hostheader-ipaddress.csv $
ホストヘッダーには以下のようなものや、実在するであろう検証には使っていないドメイン名のものがありました。検証用のサイトは特に告知などはしていないため、私が設定したドメインからアクセスした人は私一人だと思います。
name=Host, value=ec2-xx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com
仮にexample.comでサイトを公開している場合、Hostヘッダーには基本的にexample.comがセットされます。正規のユーザーの通信をブロックしていないかを確認するには、「name=Host, value=example.com」のログを抽出すると良いでしょう。
WAFのログには、IPアドレスから推測される国名が含まれます。country=JPといった形です。利用者に日本のユーザーが多い場合、日本のログを見ておくと良さそうです。今回は4件ありました。
$ cat awswaflog_exclude_hostheader-ipaddress.csv | grep "country=JP" > awswaflog_exclude_hostheader-ipaddress-JP.csv $ wc -l awswaflog_exclude_hostheader-ipaddress-JP.csv 4 awswaflog_exclude_hostheader-ipaddress-JP.csv $
通常のユーザーであれば、ChromeやEdgeなどのユーザーエージェントヘッダーがセットされるはずということでNoUserAgent_HEADERルールに一致した通信を除外します。
$ cat awswaflog_exclude_hostheader-ipaddress-JP.csv | grep -v "NoUserAgent_HEADER" > awswaflog_exclude_hostheader-ipaddress-JP_excludeNoUserAgent.csv
おさらいです。まず、ホストヘッダーにIPアドレスがセットされている場合は、除外しました。次に、IPアドレスから日本と推定されたログにしぼり、更にNoUserAgent_HEADERルールに一致した通信を除外しました。私の環境では、1件のログが残りました。
$ wc -l awswaflog_exclude_hostheader-ipaddress-JP_excludeNoUserAgent.csv 1 awswaflog_exclude_hostheader-ipaddress-JP_excludeNoUserAgent.csv $
csvファイルをスプレッドシートにインポートします。J列がrulegrouplist。M列がhttprequestです。
rulegrouplistはリクエストで動作したルールグループのリストです。今回は以下のようになりました。
[{"nonterminatingmatchingrules":[],"rulegroupid":"AWS#AWSManagedRulesAdminProtectionRuleSet","terminatingrule":"null","excludedrules":"null"}, {"nonterminatingmatchingrules":[],"rulegroupid":"AWS#AWSManagedRulesCommonRuleSet","terminatingrule":"null","excludedrules":"null"}, {"nonterminatingmatchingrules":[],"rulegroupid":"AWS#AWSManagedRulesKnownBadInputsRuleSet","terminatingrule":"null","excludedrules":"null"}, {"nonterminatingmatchingrules":[{"rulematchdetails":[],"action":"COUNT","ruleid":"LFI_URIPATH_COUNT"}],"rulegroupid":"AWS#AWSManagedRulesLinuxRuleSet","terminatingrule":{"rulematchdetails":"null","action":"BLOCK","ruleid":"LFI_URIPATH"},"excludedrules":"null"}]
注目すべきは以下です。AWSManagedRulesLinuxRuleSet中のLFI_URIPATHでブロックしたことがわかります。
["rulegroupid":"AWS#AWSManagedRulesLinuxRuleSet","terminatingrule":{"rulematchdetails":"null","action":"BLOCK","ruleid":"LFI_URIPATH"}
httprequestのuriを見ると、/etc/passwdとあることからURIでLFI攻撃を行おうとしたということがわかります。ちなみに、IPアドレスに心あたりがあり、このリクエストは私が自分でやったものであることを思い出しました。
uri=/etc/passwd, args=, httpversion=HTTP/1.1, httpmethod=GET
仮にこの通信を通過させたい場合は、AWS-AWSManagedRulesLinuxRuleSet>LFI_URIPATHのOverride rules actionを有効にします。
AWS WAFマネージドルールを利用する場合、Countモードで導入し、ログを確認しOverride rules actionを一通り設定してから、Blockに変更すると良いかと思います。また、Blockにしてからも定期的にログを確認し、Override rules actionを設定すると良いかと思います。
Override rules actionを設定するかの判断に使える例を紹介します。まず、心当たりのあるIPアドレスの場合は、関係者がシステムに攻撃をすることはないという前提でOverride rules actionに登録すると良いでしょう。次にuriが存在しないしない場合は攻撃と考えて良いかと思います。今回の検証では存在しない以下への通信がブロックされていました。
uri=/jenkins/login uri=/manager/html uri=/setup.cgi
その他、コアルールセットのSizeRestrictions_BODYはリクエスト本文のサイズをチェックするルールですが、アップロードなどを行う場合はOverride rules actionの対象になると思います。
おわりに
AWS WAFを設定し、WAFのログを確認する方法を紹介しました。検証用のサイトを2週間起動しただけで、785件の検知がありました。ログを除外した上で、rulegrouplist、httprequest、clientipに注目し、Override rules actionを有効するか検討すると良いかと思います。